<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>Interactive Diagram - Final Polished Interaction</title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" rel="stylesheet" />
    <style>
        /* CSS 变量与基础布局 */
        :root {
            --bg-color: #fff;
            --text-color: #1a1a1a;
            --border-color: #e0e0e0;
            --header-bg-color: #fafafa;
            --panel-bg-color: #fff;
            --panel-title-color: #0056b3;
            --grid-line-color: rgba(0, 0, 0, .08);
            --selected-node-stroke: #d9534f;
            --details-bg-color: #f0f0f0;
            --search-highlight-stroke: #87CEFA;
            --search-match-bg: #e6f7ff;
            --code-bg: #f3f4f6;
            --code-text: #4B5563;
            --method-badge-bg: #E0E7FF;
            --method-badge-text: #3730A3;
            --type-badge-bg: #D1FAE5;
            --type-badge-text: #065F46;
            --tooltip-bg-color: #fdfdfd;
            --toast-bg-color: #333;
            --toast-text-color: #fff;
        }

        [data-theme=dark] {
            --bg-color: #0d1117;
            --text-color: #c9d1d9;
            --border-color: #30363d;
            --header-bg-color: #161b22;
            --panel-bg-color: #161b22;
            --panel-title-color: #58a6ff;
            --grid-line-color: rgba(255, 255, 255, .08);
            --selected-node-stroke: #f07167;
            --details-bg-color: #2c313a;
            --search-highlight-stroke: #4F92B2;
            --search-match-bg: #2c313a;
            --code-bg: #1F2937;
            --code-text: #D1D5DB;
            --method-badge-bg: #3730A3;
            --method-badge-text: #E0E7FF;
            --type-badge-bg: #064E3B;
            --type-badge-text: #A7F3D0;
            --tooltip-bg-color: #1F2937;
            --toast-bg-color: #f0f0f0;
            --toast-text-color: #111;
        }

        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; display: flex; flex-direction: column; height: 100vh; margin: 0; background-color: var(--bg-color); color: var(--text-color); transition: background-color .3s, color .3s }
        .main-container { display: flex; flex: 1; overflow: hidden; position: relative; }
        #diagram-container { flex: 1; overflow: visible; text-align: center; background-color: var(--bg-color); background-image: linear-gradient(var(--grid-line-color) 1px, transparent 1px), linear-gradient(90deg, var(--grid-line-color) 1px, transparent 1px); background-size: 20px 20px }
        #diagram-container svg { width: 100%; height: 100% }
        
        #details-panel { 
            width: 450px; 
            min-width: 300px;
            max-width: 70vw;
            padding: 20px; 
            border-left: 1px solid var(--border-color); 
            background-color: var(--panel-bg-color); 
            overflow-y: auto; 
            display: none;
            position: relative; /* For resizer positioning */
        }
        #panel-resizer {
            width: 8px;
            height: 100%;
            cursor: col-resize;
            position: absolute;
            top: 0;
            left: -4px;
            background-color: transparent;
            transition: background-color 0.2s;
            z-index: 10;
        }
        #panel-resizer:hover, #panel-resizer.resizing {
            background-color: var(--panel-title-color);
            opacity: 0.5;
        }

        .panel-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-bottom: 1px solid var(--border-color);
            padding-bottom: 10px;
        }
        #details-panel h3 { margin-top: 0; padding-bottom: 0; border-bottom: none; color: var(--panel-title-color); font-size: 1.4em; flex-grow: 1; }
        #close-panel-btn {
            background: none;
            border: none;
            font-size: 2em;
            cursor: pointer;
            color: var(--text-color);
            opacity: 0.6;
            padding: 0 10px;
            line-height: 1;
        }
        #close-panel-btn:hover { opacity: 1; }
        
        details { border: none; border-bottom: 1px solid var(--border-color); margin-top: 0; border-radius: 0 }
        details:last-of-type { border-bottom: none }
        details[open] { background-color: transparent }
        
        summary { font-weight: 700; cursor: pointer; padding: 16px 8px 16px 16px; list-style: "▶ "; font-size: 1.1em; transition: background-color .2s }
        
        .category-summary { font-size: 1em; padding: 12px 8px 12px 24px; background-color: var(--details-bg-color); list-style-position: inside; }

        summary:hover { background-color: var(--details-bg-color) }
        details[open]>summary { list-style: "▼ " }
        .details-content { padding: 0 10px 16px 28px }
        .details-list { margin: 0; padding: 0; list-style-type: none }
        .details-list-item { padding: 12px; border-radius: 6px; margin-top: 10px; background-color: var(--details-bg-color) }
        .item-header { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
        .item-name-code { font-family: "Courier New", Courier, monospace; background-color: var(--code-bg); color: var(--code-text); padding: 2px 6px; border-radius: 4px; font-size: .9em }
        
        .details-list .item-name-code[data-signature] { cursor: copy; text-decoration: underline; text-decoration-style: dotted; text-decoration-color: var(--panel-title-color); }
        .search-results-list .item-name-code[data-signature] { cursor: help; }

        .method-badge, .type-badge { font-size: .75em; font-weight: 700; padding: 2px 8px; border-radius: 10px; }
        .method-badge { background-color: var(--method-badge-bg); color: var(--method-badge-text) }
        .type-badge { background-color: var(--type-badge-bg); color: var(--type-badge-text); }
        
        .item-desc { margin-top: 8px; font-size: .95em; padding-left: 4px; border-left: 2px solid var(--border-color); color: var(--text-color); opacity: .8 }
        .highlight-match { box-shadow: 0 0 0 2px var(--selected-node-stroke); border-color: var(--selected-node-stroke) !important }
        .header { display: flex; justify-content: space-between; align-items: center; padding: 10px 20px; border-bottom: 1px solid var(--border-color); background-color: var(--header-bg-color) }
        #search-box { padding: 8px 12px; border: 1px solid var(--border-color); background-color: var(--bg-color); color: var(--text-color); border-radius: 6px; width: 350px }

        #drag-hint { font-size: 0.8em; opacity: 0.6; margin-left: 20px; }

        .search-highlight>rect, .search-highlight>polygon { stroke: var(--search-highlight-stroke) !important; stroke-width: 2.5px !important }
        .selected-node>rect, .selected-node>polygon { stroke: var(--selected-node-stroke) !important; stroke-width: 3.5px !important }
        .clickable-node { cursor: pointer }
        .search-results-list, .details-list { padding: 0; margin: 0 }
        .search-result-item { padding: 12px; border-radius: 6px; margin-top: 10px; background-color: var(--details-bg-color); cursor: pointer; transition: box-shadow 0.2s; }
        .search-result-item:hover { box-shadow: 0 0 0 2px var(--search-highlight-stroke); }
        .result-node-title { font-size: 0.8em; font-weight: bold; padding: 2px 8px; border-radius: 10px; background-color: var(--method-badge-bg); color: var(--method-badge-text); margin-right: 8px; }

        #tooltip-container { position: fixed; padding: 0; border-radius: 6px; border: 1px solid var(--border-color); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); max-width: 800px; z-index: 9999; opacity: 0; visibility: hidden; transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out; pointer-events: none; overflow: hidden; background-color: var(--tooltip-bg-color); transform: scale(0.95); }
        #tooltip-container[style*="opacity: 1"] { transform: scale(1); }
        #tooltip-container pre, #tooltip-container code { font-family: "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.875em; line-height: 1.6; }
        
        #tooltip-container pre {
            margin: 0 !important;
            padding: 12px 15px;
            border-radius: 6px;
            white-space: pre-wrap;
            word-break: break-all;
        }
        
        [data-theme="light"] code[class*="language-"], [data-theme="light"] pre[class*="language-"] { color: #393A34; background: none; text-shadow: none; }
        [data-theme="light"] .token.keyword { color: #0000ff; }
        [data-theme="light"] .token.class-name, [data-theme="light"] .token.function { color: #267f99; }
        [data-theme="light"] .token.boolean, [data-theme="light"] .token.number { color: #990055; }
        [data-theme="light"] .token.operator, [data-theme="light"] .token.punctuation { color: #393A34; }
        [data-theme="light"] .token.string { color: #008000; }
        [data-theme="light"] .token.comment { color: #999; font-style: italic; }
        [data-theme="dark"] code[class*="language-"], [data-theme="dark"] pre[class*="language-"] { color: #D1D5DB; background: none; text-shadow: none; }
        [data-theme="dark"] .token.keyword { color: #c792ea; }
        [data-theme="dark"] .token.class-name, [data-theme="dark"] .token.function { color: #82aaff; }
        [data-theme="dark"] .token.boolean, [data-theme="dark"] .token.number { color: #f78c6c; }
        [data-theme="dark"] .token.operator, [data-theme="dark"] .token.punctuation { color: #89ddff; }
        [data-theme="dark"] .token.string { color: #c3e88d; }
        [data-theme="dark"] .token.comment { color: #6a737d; font-style: italic; }
        
        .node .label { white-space: pre-wrap !important; overflow-wrap: break-word !important; }
        #diagram-container .root foreignObject { overflow: visible !important;}
        #diagram-container svg text, #diagram-container svg tspan { user-select: text !important; -webkit-user-select: text !important; }
        body.alt-key-down #diagram-container svg text, body.alt-key-down #diagram-container svg tspan { user-select: none !important; -webkit-user-select: none !important; }
        body.alt-key-down #diagram-container svg { cursor: grab; }
        body.alt-key-down #diagram-container svg:active { cursor: grabbing; }

        #toast-container { position: fixed; bottom: 20px; right: 20px; z-index: 10000; display: flex; flex-direction: column; gap: 10px; }
        .toast { padding: 12px 20px; border-radius: 6px; background-color: var(--toast-bg-color); color: var(--toast-text-color); box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: toast-fade-in 0.3s ease-out; }
        .toast.fade-out { animation: toast-fade-out 0.3s ease-in forwards; }
        @keyframes toast-fade-in { from { opacity: 0; transform: translateX(100%); } to { opacity: 1; transform: translateX(0); } }
        @keyframes toast-fade-out { from { opacity: 1; transform: translateX(0); } to { opacity: 0; transform: translateX(100%); } }

    </style>
</head>
<body>
    <div class="header">
        <h1 id="main-title"></h1>
        <span id="drag-hint"></span>
        <input type="text" id="search-box">
        <button id="theme-toggle-btn"></button>
    </div>
    <div class="main-container">
        <div id="diagram-container"></div>
        <div id="details-panel">
            <div id="panel-resizer"></div>
            <div class="panel-header">
                <h3 id="details-title"></h3>
                <button id="close-panel-btn" title="Close Panel">&times;</button>
            </div>
            <div id="details-body"></div>
        </div>
    </div>
    <div id="tooltip-container"></div>
    <div id="toast-container"></div>

    <script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/svg-pan-zoom@3.6.1/dist/svg-pan-zoom.min.js"></script>
    <script src="config.js" defer></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
    <script defer>
        document.addEventListener('DOMContentLoaded', () => {
            let panZoomInstance = null;
            let mouseDownTime = 0;
            let mouseDownPos = { x: 0, y: 0 };
            
            const panel = document.getElementById('details-panel');
            const titleEl = document.getElementById('details-title');
            const bodyEl = document.getElementById('details-body');
            const searchBox = document.getElementById('search-box');
            const tooltip = document.getElementById('tooltip-container');
            const toastContainer = document.getElementById('toast-container');
            const closePanelBtn = document.getElementById('close-panel-btn');
            const resizer = document.getElementById('panel-resizer');

            function applyUiStrings() {
                document.getElementById('main-title').textContent = CONFIG.uiStrings.mainTitle;
                document.getElementById('drag-hint').textContent = CONFIG.uiStrings.dragHint;
                document.getElementById('theme-toggle-btn').textContent = CONFIG.uiStrings.themeToggleButton;
                searchBox.placeholder = CONFIG.uiStrings.searchPlaceholder;
            }

            function showToast(message, duration = 2500) {
                const toast = document.createElement('div');
                toast.className = 'toast';
                toast.textContent = message;
                toastContainer.appendChild(toast);
                setTimeout(() => {
                    toast.classList.add('fade-out');
                    toast.addEventListener('animationend', () => toast.remove());
                }, duration);
            }

            function clearAllHighlights() { document.querySelectorAll('.search-highlight, .selected-node').forEach(el => el.classList.remove('search-highlight', 'selected-node')); }
            
            function offsetDiagramForPanel(isPanelOpen) {
                if (!panZoomInstance) return;
                const sizes = panZoomInstance.getSizes();
                const pan = panZoomInstance.getPan();
                
                const diagramContainerWidth = document.getElementById('diagram-container').clientWidth;
                const panelWidth = isPanelOpen ? panel.offsetWidth : 0;
                
                const visibleAreaWidth = diagramContainerWidth - panelWidth;
                
                // The desired center for the diagram's content within the visible area
                const targetViewportX = visibleAreaWidth / 2;

                // The actual center of the diagram's content
                const diagramContentCenterX = sizes.viewBox.width / 2 * sizes.realZoom;
                
                // Calculate the required pan.x to align the diagram center with the target center
                const desiredPanX = targetViewportX - diagramContentCenterX;
                
                panZoomInstance.pan({ x: desiredPanX, y: pan.y });
            }

            function closeDetailsPanel() {
                panel.style.display = 'none';
                clearAllHighlights();
                offsetDiagramForPanel(false);
            }

            function handleNodeClick(nodeId, highlightItemName = null) { 
                clearAllHighlights(); 
                const nodeElement = document.querySelector(`g[data-node-id="${nodeId}"]`); 
                if (nodeElement) { 
                    nodeElement.classList.add('selected-node'); 
                } 
                showNodeDetails(nodeId, highlightItemName); 
            }
            
            async function initializeDiagram() {
                const currentTheme = document.body.getAttribute('data-theme') === 'dark' ? 'dark' : 'default';
                mermaid.initialize({ startOnLoad: false, theme: currentTheme, flowchart: { useMaxWidth: false } });
                
                const { svg } = await mermaid.render('interactive-diagram-svg', CONFIG.diagramDefinition);
                const container = document.getElementById('diagram-container');
                container.innerHTML = svg;
                const svgElement = container.querySelector('svg');
                
                if (svgElement) {
                    if (panZoomInstance) panZoomInstance.destroy();
                    panZoomInstance = svgPanZoom(svgElement, { 
                        zoomEnabled: true, 
                        controlIconsEnabled: true, 
                        fit: true, 
                        center: true, 
                        contain: true, 
                        panEnabled: false, 
                        preventMouseEventsDefault: false 
                    });
                    svgElement.addEventListener('mousedown', (event) => { 
                        mouseDownTime = Date.now(); 
                        mouseDownPos = { x: event.clientX, y: event.clientY }; 
                        if (event.altKey && event.button === 0) { 
                            panZoomInstance.enablePan(); 
                        } 
                    });
                    svgElement.addEventListener('mouseup', (event) => { 
                        if (event.button !== 0) return; 
                        panZoomInstance.disablePan(); 
                        const timeDiff = Date.now() - mouseDownTime; 
                        const dist = Math.hypot(event.clientX - mouseDownPos.x, event.clientY - mouseDownPos.y); 
                        if (timeDiff < 200 && dist < 5 && !event.altKey) { 
                            const targetNode = event.target.closest('g.clickable-node'); 
                            if (targetNode) { 
                                const nodeId = targetNode.getAttribute('data-node-id'); 
                                if (nodeId) { handleNodeClick(nodeId); } 
                            } 
                        } 
                    });
                }
                
                for (const nodeId in CONFIG.nodeDetails) {
                    const selector = `g[id*="-${nodeId}-"]`;
                    const nodeElement = document.querySelector(selector);
                    if (nodeElement) { 
                        nodeElement.classList.add('clickable-node'); 
                        nodeElement.setAttribute('data-node-id', nodeId); 
                    }
                }
            }
            
            function createListItemHtml(item, type, highlightItemName) {
                const highlightClass = item.name === highlightItemName ? 'highlight-match' : '';
                let badgesHtml = '';
                if (type === 'method') {
                    badgesHtml = `<span class="method-badge">Method</span>`;
                } else if (type === 'variable' && item.type) {
                    badgesHtml = `<span class="type-badge">${item.type}</span>`;
                }
                const signatureAttr = item.signature ? `data-signature="${item.signature.replace(/"/g, '&quot;')}"` : '';
                return `<li class="details-list-item ${highlightClass}"><div class="item-header"><span class="item-name-code" ${signatureAttr}>${item.name}</span>${badgesHtml}</div><p class="item-desc">${item.desc}</p></li>`;
            }

            function createSection(title, data, type, highlightItemName) {
                if (!data || data.length === 0) return '';
                const isCategorized = data[0].hasOwnProperty('category');
                let contentHtml;
                if (isCategorized) {
                    contentHtml = data.map(cat => {
                        const itemsHtml = cat.items.map(item => createListItemHtml(item, type, highlightItemName)).join('');
                        return `<details open><summary class="category-summary">${cat.category}</summary><ul class="details-list" style="margin-left: 10px; margin-top: 10px;">${itemsHtml}</ul></details>`;
                    }).join('');
                } else {
                    const itemsHtml = data.map(item => createListItemHtml(item, type, highlightItemName)).join('');
                    contentHtml = `<ul class="details-list">${itemsHtml}</ul>`;
                }
                return `<details open><summary>${title}</summary><div class="details-content">${contentHtml}</div></details>`;
            }

            function showNodeDetails(nodeId, highlightItemName = null) {
                const details = CONFIG.nodeDetails[nodeId];
                if (!details) { 
                    closeDetailsPanel();
                    return; 
                }
                titleEl.innerText = details.title || nodeId;
                bodyEl.innerHTML = '';
                bodyEl.innerHTML += createSection(CONFIG.uiStrings.variablesTitle, details.variables, 'variable', highlightItemName);
                bodyEl.innerHTML += createSection(CONFIG.uiStrings.methodsTitle, details.methods, 'method', highlightItemName);
                
                const wasHidden = panel.style.display === 'none';
                panel.style.display = 'block';

                if(wasHidden) {
                    setTimeout(() => offsetDiagramForPanel(true), 50);
                }
            }
            
            function showSearchResults(matches) { 
                clearAllHighlights(); 
                const matchedNodeIds = new Set(matches.map(m => m.nodeId)); 
                matchedNodeIds.forEach(id => { const el = document.querySelector(`g[data-node-id="${id}"]`); if(el) el.classList.add('search-highlight'); }); 
                titleEl.innerText = CONFIG.uiStrings.searchResultsTitle(matches.length); 
                if (matches.length === 0) { 
                    bodyEl.innerHTML = `<p>${CONFIG.uiStrings.noResults}</p>`; 
                } else { 
                    const listItems = matches.map(match => { 
                        const nodeDetails = CONFIG.nodeDetails[match.nodeId]; 
                        const allItems = [...(nodeDetails?.variables || []), ...(nodeDetails?.methods || [])].flatMap(item => item.category ? item.items : item);
                        const itemDetails = allItems.find(i => i.name === match.itemName);
                        const signatureAttr = itemDetails?.signature ? `data-signature="${itemDetails.signature.replace(/"/g, '&quot;')}"` : ''; 
                        let badgeHtml = ''; 
                        const isMethod = (nodeDetails?.methods || []).flatMap(m => m.category ? m.items : m).some(m => m.name === match.itemName);
                        if (isMethod) { badgeHtml = `<span class="method-badge">Method</span>`; } 
                        else if (itemDetails?.type) { badgeHtml = `<span class="type-badge">${itemDetails.type}</span>`; } 
                        return `<li class="search-result-item" data-node-id="${match.nodeId}" data-item-name="${match.itemName}"><div class="item-header"><span class="result-node-title">${nodeDetails.title}</span><span class="item-name-code" ${signatureAttr}>${match.itemName}</span>${badgeHtml}</div><p class="item-desc">${match.itemDesc}</p></li>`; 
                    }).join(''); 
                    bodyEl.innerHTML = `<ul class="search-results-list">${listItems}</ul>`; 
                } 
                
                const wasHidden = panel.style.display === 'none';
                panel.style.display = 'block'; 

                if(wasHidden) {
                     setTimeout(() => offsetDiagramForPanel(true), 50);
                }

                document.querySelectorAll('.search-result-item').forEach(item => { 
                    item.addEventListener('click', () => { 
                        const targetNodeId = item.getAttribute('data-node-id'); 
                        const targetItemName = item.getAttribute('data-item-name'); 
                        handleNodeClick(targetNodeId); 
                        showNodeDetails(targetNodeId, targetItemName); 
                    }); 
                }); 
            }
            
            function performSearch() { 
                const queryTerms = searchBox.value.trim().toLowerCase().split(' ').filter(term => term.length > 0); 
                if (queryTerms.length < 1 && searchBox.value.trim() !== '*') { 
                    closeDetailsPanel();
                    return []; 
                } 
                const matches = []; 
                for (const nodeId in CONFIG.nodeDetails) { 
                    const details = CONFIG.nodeDetails[nodeId]; 
                    const collections = [...(details.variables || []), ...(details.methods || [])];
                    const items = collections.flatMap(item => item.category ? item.items : item);
                    items.forEach(item => { 
                        const itemNameLower = item.name.toLowerCase(); 
                        if (queryTerms.every(term => itemNameLower.includes(term))) { 
                            matches.push({ nodeId, itemName: item.name, itemDesc: item.desc }); 
                        } 
                    }); 
                } 
                return matches; 
            }

            function setupPanelEventListeners() {
                panel.addEventListener('mouseover', (event) => {
                    const target = event.target.closest('.item-name-code[data-signature]');
                    if (!target) return;
                    const signature = target.getAttribute('data-signature');
                    let finalHtml;
                    try {
                        const highlightedHtml = Prism.highlight(signature, Prism.languages.cpp, 'cpp');
                        finalHtml = `<pre class="language-cpp"><code class="language-cpp">${highlightedHtml}</code></pre>`;
                    } catch (e) {
                        console.warn("Prism syntax highlighting failed:", e);
                        const escapedSignature = signature.replace(/</g, "&lt;").replace(/>/g, "&gt;");
                        finalHtml = `<pre class="language-cpp"><code>${escapedSignature}</code></pre>`;
                    }
                    tooltip.innerHTML = finalHtml;
                    tooltip.style.visibility = 'visible';
                    const tooltipRect = tooltip.getBoundingClientRect();
                    const targetRect = target.getBoundingClientRect();
                    const margin = 8;
                    let top = targetRect.top - tooltipRect.height - margin;
                    if (top < margin) { top = targetRect.bottom + margin; }
                    let left = targetRect.left + (targetRect.width / 2) - (tooltipRect.width / 2);
                    if (left < margin) { left = margin; } 
                    else if (left + tooltipRect.width > window.innerWidth - margin) { left = window.innerWidth - tooltipRect.width - margin; }
                    tooltip.style.top = `${top}px`;
                    tooltip.style.left = `${left}px`;
                    tooltip.style.opacity = '1';
                });

                panel.addEventListener('mouseout', (event) => {
                    const target = event.target.closest('.item-name-code[data-signature]');
                    if (target) {
                        tooltip.style.opacity = '0';
                        tooltip.style.visibility = 'hidden';
                    }
                });

                panel.addEventListener('click', (event) => {
                    const target = event.target.closest('.details-list .item-name-code[data-signature]');
                    if (!target) return;
                    const signature = target.getAttribute('data-signature');
                    if (navigator.clipboard) {
                        navigator.clipboard.writeText(signature).then(() => {
                            showToast(CONFIG.uiStrings.copySuccessToast);
                        }).catch(err => {
                            console.error('无法复制到剪贴板', err);
                            showToast(CONFIG.uiStrings.copyFailToast, 2500);
                        });
                    }
                });
            }

            // --- Panel Resizing Logic ---
            const initResizer = function(e) {
                e.preventDefault();
                resizer.classList.add('resizing');
                window.addEventListener('mousemove', startResizing);
                window.addEventListener('mouseup', stopResizing);
            };

            const startResizing = function(e) {
                const newWidth = window.innerWidth - e.clientX;
                const minWidth = 300; // From CSS
                const maxWidth = window.innerWidth * 0.7; // From CSS
                
                if (newWidth > minWidth && newWidth < maxWidth) {
                    panel.style.width = newWidth + 'px';
                    offsetDiagramForPanel(true);
                }
            };

            const stopResizing = function() {
                resizer.classList.remove('resizing');
                window.removeEventListener('mousemove', startResizing);
                window.removeEventListener('mouseup', stopResizing);
            };

            // --- 初始化流程 ---
            const savedTheme = localStorage.getItem('theme') || 'light';
            document.body.setAttribute('data-theme', savedTheme);
            
            const themeToggleBtn = document.getElementById('theme-toggle-btn');
            themeToggleBtn.addEventListener('click', () => { 
                const newTheme = document.body.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'; 
                document.body.setAttribute('data-theme', newTheme); 
                localStorage.setItem('theme', newTheme); 
                initializeDiagram(); 
            });

            window.addEventListener('keydown', (event) => { if (event.key === 'Alt') { event.preventDefault(); document.body.classList.add('alt-key-down'); } });
            window.addEventListener('keyup', (event) => { if (event.key === 'Alt') { document.body.classList.remove('alt-key-down'); } });

            function triggerSearch() { const matches = performSearch(); showSearchResults(matches); }
            searchBox.addEventListener('input', triggerSearch);
            searchBox.addEventListener('keydown', (event) => { if (event.key === 'Enter') triggerSearch(); });
            
            closePanelBtn.addEventListener('click', closeDetailsPanel);
            resizer.addEventListener('mousedown', initResizer);

            applyUiStrings();
            setupPanelEventListeners();
            initializeDiagram();
        });
    </script>
</body>
</html>